using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

using GameBoardViewer.GameBoardViewerControl;
using GameEngine;
using CXEngineWrapper;
using DaphneWin8.Common;

namespace DaphneWin8
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class MainGame : Game
    {
        GraphicsDeviceManager _graphics;
        SpriteBatch _spriteBatch;
        GameBoardViewerControl _ctrl;
        GameManager _mng;
        Engine _engine;
        ButtonState m_LeftButtonState;
        ButtonState m_RightButtonState;
        EngineMoveConverter _converter;

        public MainGame()
        {
            _graphics = new GraphicsDeviceManager(this);
            _graphics.PreferMultiSampling = true;
            _graphics.PreparingDeviceSettings += _graphics_PreparingDeviceSettings;
            Content.RootDirectory = "Content";
            _ctrl = new GameBoardViewerControl(this);
            _mng = GameManagerFactory.CreateGameManager(enumGameType.Czech);
            _mng.GameBoard.SetupStartPosition(3);
            _ctrl.SetData(_mng, "Daphne 2013 Win 8 Edition");
            this.Window.Title = "Daphne 2013 Win 8 Edition";
            
            m_LeftButtonState = ButtonState.Released;
            m_RightButtonState = ButtonState.Released;

            _converter = new EngineMoveConverter();
            _engine = new Engine();
            //_engine.LoadOpeningDatabase(
            _engine.MoveComputed += _engine_MoveComputed;

            _ctrl.GameMoveDoneEvent += _ctrl_GameMoveDoneEvent;
        }

        void _engine_MoveComputed(OutputBitBoard bb)
        {
            // convert to move to list of positions
            var list = _converter.Convert(bb, _mng.GameBoard);

            Position startPos = list[0];
            Position lastPos = list[1];
            list.Remove(startPos);
            list.Remove(lastPos);

            Move move = _mng.CompleteMoveStep(startPos, lastPos);
            _mng.DoMoveStep(move);

            while (list.Count > 0)
            {
                Position finPos = list[0];
                Move curMove = _mng.CompleteMoveStep(lastPos, finPos);
                _mng.DoMoveStep(curMove);

                Move lastMove = move;
                while (lastMove.nextMove != null)
                    lastMove = lastMove.nextMove;
                lastMove.nextMove = curMove;

                lastPos = finPos;
                list.Remove(finPos);
            }

            _mng.FinalizeMove(move);
            _mng.MoveHistory.MoveBack();

            _ctrl.AnimateMove(move);

            //_mng.MoveHistory.MoveForward();
        }

        void _ctrl_GameMoveDoneEvent(object sender, GameBoardViewer.Common.GameMoveDoneEventArgs e)
        {
            _mng.FinalizeMove(e.move);

            DoComputerMove();          
        }

        void DoComputerMove()
        {
            // convert board
            var checkersBoard = _converter.ConvertBoard(_mng.GameBoard);

            // get game history
            List<Move> lstGameHistory = new List<Move>();
            _mng.MoveHistory.GetMovesDone(lstGameHistory);

            _engine.ComputeMove(checkersBoard, _mng.GameBoard.OnMove == enumFigureColor.White,
                            2000, _converter.ConvertMoveHistory(lstGameHistory, _mng.GameBoard));
        }


        void _graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
        {
            e.GraphicsDeviceInformation.DeviceType = DeviceType.Hardware;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            _ctrl.Initialize();

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            _ctrl.LoadContent();

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            _ctrl.UnloadContent();
            //_engine.TerminateComputer();
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            _ctrl.Update(gameTime);

            MouseState mouseState = Microsoft.Xna.Framework.Input.Mouse.GetState();

            Point mousePoint = new Point(mouseState.X, mouseState.Y);

            // check buttons pressed
            if (m_LeftButtonState == ButtonState.Released && mouseState.LeftButton == ButtonState.Pressed)
                _ctrl.OnLButtonDown(mousePoint);
            if (m_RightButtonState == ButtonState.Released && mouseState.RightButton == ButtonState.Pressed)
                _ctrl.OnRButtonDown(mousePoint);           

            // signal mouse is moving
            _ctrl.OnMouseMoving(mousePoint);

            // check buttons released
            if (m_LeftButtonState == ButtonState.Pressed && mouseState.LeftButton == ButtonState.Released)
                _ctrl.OnLButtonUp(mousePoint);
            if (m_RightButtonState == ButtonState.Pressed && mouseState.RightButton == ButtonState.Released)
                _ctrl.OnRButtonUp(mousePoint);

            // reset button states
            m_LeftButtonState = mouseState.LeftButton;
            m_RightButtonState = mouseState.RightButton;

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            _ctrl.Draw(gameTime);

            base.Draw(gameTime);
        }
    }
}
